﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using Sinacor.Infra.UI.Controls.SmartClient;
using System.Windows.Threading;
using System.Windows;
using System.Diagnostics;
using EL = Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System.IO;
using Sinacor.Infra.UI.Desktop.SmartClient;
using Sinacor.Infra.UI.Session;
using Sinacor.Infra.UI.Messaging.Internal;
using Sinacor.Infra.UI.Messaging;
using Sinacor.Infra.UI.Messaging.SmartClient;

namespace Sinacor.UI.Design.Tester.View.SmartClient
{
    public struct NewWindow
    {
        public string AssemblyName;
        public string ClassName;
		public List<string> MenuPath;
    }

    public class WindowController : DispatcherObject
    {
		public bool OpenWindow(string assembly, string className, List<string> menuPath)
        {
            // Instancia uma nova Thread
            Thread newWindowThread = new Thread(new ParameterizedThreadStart(SinacorWindow_ParameterizedThreadStart));
            //Define o ApartmentState como STA (requisito do WPF)
            newWindowThread.SetApartmentState(ApartmentState.STA);

            // Chama a função que inicia a nova Thread
            try
            {
                NewWindow nw = new NewWindow();
                nw.AssemblyName = assembly;
                nw.ClassName = className;
				nw.MenuPath = menuPath;

                //Inicializa a thread.
                newWindowThread.Start(nw);
            }
			catch (Exception ex)
			{
				#if DEBUG
				Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show("An error has occurred while trying to open a window\nFor a detailed information about the error check the event viewer", "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
				EventLog.WriteEntry("SinacorDesenv", FormatExceptionMessage(ex), EventLogEntryType.Error);
				#else
				Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show("Error trying to open a new window", "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
				#endif
				return false;
			}
            return true;
        }

        private void InitializeWindow(string moduleName, string assemblyName, string className, List<string> menuPath)
        {
            Assembly assembly = null;
            Type typWindowType = null;
            object window = null;

            try
            {
                string windowPath = "";
                if (assemblyName != "Sinacor.UI.Design.Tester.View.SmartClient.exe")
                    windowPath = new Uri(new Uri(System.Windows.Forms.Application.StartupPath + @"\"),
                                               moduleName + @"/" + assemblyName).LocalPath;
                else
                    windowPath = new Uri(new Uri(System.Windows.Forms.Application.StartupPath + @"\"),
                                               assemblyName).LocalPath;

                // Carrega o Assembly que contém a Janela a partir do disco
                assembly = Assembly.LoadFrom(windowPath);
                //assembly = Assembly.LoadFrom(moduleName + "\\" + assemblyName);
            }
			catch (Exception ex)
			{
				#if DEBUG
				Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show("An error has occurred while trying to Load an assembly\nFor a detailed information about the error check the event viewer", "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
				EventLog.WriteEntry("SinacorDesenv", FormatExceptionMessage(ex), EventLogEntryType.Error);
				#else
				Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show(String.Format("Error trying to load the assembly \"{0}\"", assemblyName), "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
				#endif
				return;
			}

            // Obtém o tipo da Janela
            string fullAssemblyName = className;
            typWindowType = assembly.GetType(fullAssemblyName);
			bool windowIsOpened = false;

			if (typWindowType == null)
			{
				//TODO: Criar uma classe de exception customizada
				Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show(String.Format("The class {0} was not found in the assembly {1}", className, assemblyName), "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
				return;
			}
			else
			{
				DesktopVirtual _virtualDesktop = DesktopVirtual.GetInstance();
				SessionManager _sessionaManager = SessionManager.GetInstance();
				WPFMessageManager _messageManager = new WPFMessageManager();

				foreach (SinacorWindow w in _virtualDesktop.GetDesktopVirtual(_sessionaManager.GetCurrentSession().TokenID))
				{
					if (w.GetType() == typWindowType)
					{
						if (IsWindowOwnerNull(w) && !WindowAcceptsMultipleInstances(w))
						{
							windowIsOpened = true;

							InternalMessage message = new InternalMessage();
							message.MessageId = MessageIds.WindowManager.SNC_WINDOW_RESTORE;
							message.Message = w.GetHashCode();
							_messageManager.SendMessage(message);
							break;
						}
					}
				}

                //liberar as intancias
                _virtualDesktop = null;
                _sessionaManager = null;
                _messageManager = null;
			}

			if (!windowIsOpened)
			{
				try
				{
					// Define parâmetros que serão usados pelo Constructor
					Type[] ctorParameter = Type.EmptyTypes;
					// Obtém informações sobre o Constructor
					ConstructorInfo ctor = typWindowType.GetConstructor(ctorParameter);
					// Instancia a nova janela
					window = ctor.Invoke(new object[] { });
				}
				catch (Exception ex)
				{
					#if DEBUG
					Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show("An error has occurred while trying to create a window\nFor a detailed information about the error check the event viewer", "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
					EventLog.WriteEntry("SinacorDesenv", FormatExceptionMessage(ex), EventLogEntryType.Error);
					#else
					Sinacor.Infra.UI.Controls.SmartClient.SinacorMessageBox.Show("Error trying to create window", "ERROR", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
					#endif
					return;
				}

				//Armazena a Janela.
			    SinacorWindow sinacorWindow = (SinacorWindow)window;
                sinacorWindow.MenuHeader = menuPath;
                sinacorWindow.Show();
                sinacorWindow.RunDispatcher();
                sinacorWindow = null;
			}
        }

        private void SinacorWindow_ParameterizedThreadStart(object state)
        {
            NewWindow st = (NewWindow)state;
            this.InitializeWindow(RetrieveModuleName(st.AssemblyName), st.AssemblyName, st.ClassName, st.MenuPath);
        }


		delegate bool IsWindowOwnerNullDelegate(SinacorWindow window);
		private bool IsWindowOwnerNull(SinacorWindow window)
		{
			if (System.Threading.Thread.CurrentThread != window.Dispatcher.Thread)
			{
				IsWindowOwnerNullDelegate dlg = new IsWindowOwnerNullDelegate(IsWindowOwnerNull);
				return (bool)window.Dispatcher.Invoke(dlg, window);
			}
			else
				return (window.Owner == null);
		}

		delegate bool WindowAcceptsMultipleInstancesDelegate(SinacorWindow window);
		private bool WindowAcceptsMultipleInstances(SinacorWindow window)
		{
			if (System.Threading.Thread.CurrentThread != window.Dispatcher.Thread)
			{
				WindowAcceptsMultipleInstancesDelegate dlg = new WindowAcceptsMultipleInstancesDelegate(WindowAcceptsMultipleInstances);
				return (bool)window.Dispatcher.Invoke(dlg, window);
			}
			else
				return window.AcceptsMultipleInstance;
		}

        //private string RetrieveAssemblyFullPath(string assemblyName)
        //{
        //    //Sinacor.UI.Modulo.SubModulo.View.SmartClient
        //    if (!string.IsNullOrEmpty(assemblyName))
        //        return RetrieveModuleName(assemblyName) + @"\" + assemblyName;
        //    else
        //        return null;

        //}

        private static string RetrieveModuleName(string assemblyName)
        {
            return assemblyName.Split('.')[2];
        }

		/// <summary>
		/// Returns a text representation of the exception.
		/// </summary>
		/// <param name="exception">The exception to be formatted.</param>
		/// <returns>Exception formatted as a text.</returns>
		private string FormatExceptionMessage(Exception exception)
		{
			using (StringWriter sw = new StringWriter())
			{
				EL.TextExceptionFormatter exceptionFormatter = new EL.TextExceptionFormatter(sw, exception);
				exceptionFormatter.Format();
				return sw.ToString();
			}
		}
    }
}